/*
*    Qizx/open 3.0 beta
*
* This code is the open-source version of Qizx.
* Copyright (C) 2004-2009 Axyana Software -- All rights reserved.
*
* The contents of this file are subject to the Mozilla Public License 
*  Version 1.1 (the "License"); you may not use this file except in 
*  compliance with the License. You may obtain a copy of the License at
*  http://www.mozilla.org/MPL/
*
* Software distributed under the License is distributed on an "AS IS" basis,
*  WITHOUT WARRANTY OF ANY KIND, either express or implied. See the License
*  for the specific language governing rights and limitations under the
*  License.
*
* The Initial Developer of the Original Code is Xavier Franc - Axyana Software.
*
*/
using System;
using Node = System.Qizx.Api.Node;
using FullTextFactory = System.Qizx.Api.FullText.FullTextFactory;
using Scorer = System.Qizx.Api.FullText.Scorer;
using Utils = System.Qizx.Util.Basic.Utils;
using FONIDocument = System.Qizx.Xdm.FONIDocument;
namespace System.Qizx.Queries.Iterators {

    /// <summary>	Base class for all implementations of PostingIterator</summary>
    public abstract class PostingIteratorBase : PostingIterator {
        virtual public int DocId {
            get {
                return curDocId;
            }

        }
        virtual public int NodeId {
            get {
                return curNodeId;
            }

        }
        virtual public int NodeEnd {
            get {
                if (curNodeId < 0) {
                    skipToNode(0, System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID);
                }
                return curNodeId + NodeSpan;
            }

        }
        virtual public int PrevNodeEnd {
            get {
                return prevNodeEnd;
            }

        }
        virtual public int Depth {
            get {
                return 0; // default
            }

        }
        virtual public int NodeSpan {
            get {
                return 0; // default
            }

        }
        virtual public bool Fulltext {
            set {
                this.isFulltext = value;
            }

        }
        virtual public float Weight {
            set {
                this.weight = value;
            }

        }
        virtual public Filter[] Filters {
            //  public boolean checkDepths(ContainerIterator container)
            //  {
            //      return basicCheckDepth(container);
            //  }


            get {
                return filters;
            }

            set {
                this.filters = value;
            }

        }
        virtual public int DepthTest {
            set {
                this.depthTest = value;
            }

        }
        protected internal Filter[] filters;
        protected internal int depthTest = -1;
        protected internal ContainerIterator container;

        protected internal bool ordered; // used in PolyIter, WildCard, Or
        //protected boolean resetState; // set by resetToNode
        protected internal bool isFulltext;

        protected internal int curDocId = -1;
        protected internal int curNodeId = -1;
        protected internal int prevNodeEnd = -1;

        // FT scoring:
        protected internal float weight = 1; // optional, default 1 
        protected internal float weightNorm; // weight * normalization

        // cached document data model
        protected internal FONIDocument dm;

        public virtual FONIDocument currentDataModel() {
            return dm;
        }

        public virtual void resetDoc() {
            curDocId = -1;
            curNodeId = -1;
        }

        public virtual bool goNextDoc() {
            dm = null;
            return skipToDoc(curDocId + 1);
        }


        public virtual bool reachedEnd() {
            return curNodeId >= System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID;
        }

        // default implementation: ContainerIter really implements it 
        public virtual double getFulltextScore(Node node) {
            return -1;
        }

        public virtual float computeWeighting(Scorer scorer) {
            System.Console.Error.WriteLine("OOPS PostingIteratorBase.computeWeighting !!!");
            return 1; // should not happen
        }

        public virtual float computeScore(Scorer scorer) {
            throw Utils.shouldNotHappen();
        }

        public virtual bool checkWordDistance(int/*NId*/ posting1, int/*NId*/ posting2,
                                     int offset, int min, int max) {

            throw Utils.shouldNotHappen();
        }

        public virtual bool checkBoundary(int posting, int boundary, bool start) {
            throw Utils.shouldNotHappen();
        }

        protected internal virtual void changeDoc(int docId) {
            if (curDocId != docId)
                curNodeId = -1;
            curDocId = docId;
        }

        // VERY IMPORTANT : set curDocId to max when end reached
        protected internal virtual bool noMoreDocs() {
            curDocId = System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_DOCID;
            return false;
        }

        // VERY IMPORTANT : set curNodeId to max when end reached
        protected internal virtual bool noMoreNodes() {
            curNodeId = System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID;
            return false;
        }

        protected internal virtual bool noMoreNodes(int limit) {
            if (limit == System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID)
                curNodeId = limit;
            return false;
        }

        public virtual bool nextNode() {
            for (; ; ) {
                for (; goNextNode(System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID); ) {
                    if (checkFilters())
                        return true;
                }
                dm = null;
                if (!skipToDoc(curDocId + 1))
                    return false;
            }
        }

        public virtual bool goNextNode(int limit)
            // intra-doc
        {
            if (curDocId < 0) {
                if (!skipToDoc(0))
                    return false;
            } else if (curDocId >= System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_DOCID)
                return false;
            return skipToNode(curNodeId + 1, limit);
        }

        public virtual bool skipToNode(int nodeId, int limit) {
            if (curNodeId >= limit)
                return false;
            // if we are beyond curNodeId, dont move
            // (curNodeId must be ON A REAL HIT or be a dummy value MAX_NODEID]
            // NOTE: causes problems with Phrase => redefined
            if (curNodeId >= nodeId && checkFilters()) {
                return true; // need to recheck due to depth
            }
            prevNodeEnd = curNodeId + NodeSpan; // OPTIM do it only if some flag set
            while (basicSkipToNode(nodeId, limit)) {
                if (checkFilters()) {
                    return true;
                }
                if (curNodeId >= limit)
                    return false;
                nodeId = curNodeId + 1;
            }
            return false;
        }

        abstract protected internal bool basicSkipToNode(int nodeId, int limit);


        public virtual bool skipToEnclosing(int nodeId) {
            if (curNodeId < 0)
                if (!skipToNode(0, System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID))
                    return false;
            int end = NodeEnd, cur = curNodeId;
            int cnt = 0;
            while (end < nodeId) {
                // -System.err.println("container.nextNode
                // "+container.getNodeId());
                if (!basicSkipToNode(cur + 1, System.Qizx.Queries.Iterators.PostingIterator_Fields.MAX_NODEID)) {
                    // avoid filter
                    return false;
                }
                cur = NodeId;
                end = NodeEnd;
                ++cnt;
            }
            // -System.err.println(nodeId+" skipped "+cnt);
            return true;
        }

        // generic implementation: skipTo(start) and before end
        // Assumes that rangeStart will never decrease
        public virtual bool inRange(int rangeStart, int rangeEnd) {
            return skipToNode(rangeStart, rangeEnd) && NodeId < rangeEnd;
        }

        // generic implementation: if not already beyond, try to go to next
        public virtual bool nextBefore(int rangeEnd) {
            for (; ; ) {
                // always move, unless already too far:
                if (NodeId >= rangeEnd || !goNextNode(rangeEnd))
                    return false;
                // test inclusion
                if (NodeId < rangeEnd)
                    return true;
            }
        }

        public virtual void initContainer(ContainerIterator container, FullTextFactory scoringFactory) {
            if (depthTest != -1)
                this.container = container;
            if (isFulltext)
                container.addFulltextQuery(this);
        }

        public virtual bool checkFilters() {
            if (container != null && !basicCheckDepth(container))
                return false;

            if (filters != null)
                for (int f = filters.Length; --f >= 0; )
                    if (!filters[f].check(this))
                        return false;
            return true;
        }

        protected internal virtual bool basicCheckDepth(ContainerIterator container) {
            int refDepth = (container == null) ? 0 : container.Depth;
            int ddiff = Depth - refDepth;
            if (ddiff < 0) {
                return false;
            }

            // depthTest < 0 means depth-diff must be at least  -1 - depthTest
            // depthTest >= 0 means exactly equal
            return (depthTest < 0) ? (ddiff >= -1 - depthTest) : (ddiff == depthTest);
        }

        public virtual void addFilter(Filter filter) {
            if (filters == null)
                filters = new Filter[] { filter };
            else {
                Filter[] old = filters;
                filters = new Filter[old.Length + 1];
                Array.Copy(old, 0, Filters, 0, old.Length);
                filters[old.Length] = filter;
            }
        }

        protected internal virtual PostingIterator copyFilters(PostingIterator clone) {
            // problem: need to clone the filters or not?
            clone.Filters = filters;
            clone.DepthTest = depthTest;
            if (clone is PostingIteratorBase)
                ((PostingIteratorBase)clone).isFulltext = isFulltext;
            return clone;
        }

        internal static PostingIterator[] bornAgain(PostingIterator[] iterators) {
            if (iterators == null)
                return null;
            return bornAgain(iterators, iterators.Length);
        }

        protected internal static PostingIterator[] bornAgain(PostingIterator[] iterators, int iterCount) {
            if (iterators == null)
                return null;
            PostingIterator[] nit = new PostingIterator[iterCount];
            for (int i = iterators.Length; --i >= 0; ) {
                PostingIterator it = iterators[i];
                if (it != null)
                    nit[i] = it.bornAgain();
            }
            return nit;
        }

        public virtual void debug() {
            for (; nextNode(); ) {
                System.Console.Error.WriteLine("node " + NodeId + "-" + NodeEnd + " d=" + Depth);
            }
        }

        public virtual bool isOrdered() {
            return ordered;
        }

        public virtual void setOrdered(bool ordered) {
            this.ordered = ordered;
        }

        public virtual bool segmentOverlap(PostingIterator iter) {
            return false;
        }
        public abstract System.Qizx.Queries.Iterators.PostingIterator bornAgain();
        public abstract void resetToNode(int param1);
        public abstract bool skipToDoc(int param1);
    }
}